package org.altervista.cp.thinjdbc;

import java.util.LinkedList;

import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.altervista.cp.thinjdbc.util.LogHelper;
import org.altervista.cp.thinjdbc.wrappers.Connection;
import org.altervista.cp.thinjdbc.wrappers.DataSource;

public class Session {

	private static final LogHelper logger = new LogHelper(Session.class);

	static final Session current() {
		return _TL.get();
	}

	public Session(Connection cw) {
		logger.debug("Starting new session");
		if (_TL.get() != null) {
			logger.debug("Found a previously open session: don't forget it!");
		}
		_cw = cw;
		_TL.set(this);
	}

	public Session(String dsJNDIName) throws NamingException {
		this(((DataSource) new InitialContext().lookup(dsJNDIName))
				.getConnection());
	}

	public void begin() {
		if (_cw.getAutoCommit()) {
			_unsetAutoCommit = true;
			_cw.setAutoCommit(false);
			logger.debug("Begin new transaction");
		}
	}

	private void end() {
		if (_unsetAutoCommit) {
			_unsetAutoCommit = false;
			_cw.setAutoCommit(true);
		}
	}

	public void close() {
		_TL.set(null);
		for (Query<?> q : openQueryList) {
			q.clearParameters();
		}
		if (_commitTransactionOnClose) {
			commit();
		} else {
			end();
		}
		_cw.close();
	}

	public void commit() {
		if (_unsetAutoCommit) {
			logger.debug("Committing transaction");
			_cw.commit();
			logger.debug("Transaction has been committed successfully");
			end();
		} else {
			logger.debug("Autocommit has not been unset: commit skipped");
		}
	}

	public Connection getConnection() {
		return _cw;
	}

	public boolean isCommitTransactionOnClose() {
		return _commitTransactionOnClose;
	}

	public void rollback() {
		if (_unsetAutoCommit) {
			logger.debug("Rollbacking transaction");
			_cw.rollback();
			logger.debug("Transaction has been rollbacked successfully");
			end();
		} else {
			logger.debug("Autocommit has not been unset: roolback skipped");
		}
	}

	public void setCommitTransactionOnClose(boolean commitTransactionOnClose) {
		_commitTransactionOnClose = commitTransactionOnClose;
	}

	void addOpenQuery(Query<?> q) {
		openQueryList.add(q);
	}

	protected void finalize() {
		close();
	}

	private boolean _commitTransactionOnClose;
	private boolean _unsetAutoCommit;
	private Connection _cw;
	private LinkedList<Query<?>> openQueryList = new LinkedList<Query<?>>();

	private static final ThreadLocal<Session> _TL = new ThreadLocal<Session>();
}
